#!/usr/bin/env python3.6.0
# -*- coding: utf-8 -*-
"""
Created on 2020/8/5

@author: 260190

@description:
功能说明：上传待对比的文件，设置检测精度，比较文件差异，输出对比结果。
备注：代码需要在linux环境下运行，pdf2image模块不能在win7下运行
1、上传文件和对比结果保存在upload文件夹下
2、upload/pic目录下 三个图片文件夹手动创建，org_jpgs、mod_jpgs、comp_jpgs分别存改前文件、改后文件、对比结果的图片。
3、检测过程中生成的图片保存在upload/pic文件夹下，每次检测单独生成一文件夹，检测结束若无异常删除该图片文件夹，
若有异常将文件移动至upload/Exception目录下。（异常指未生成对比结果）

"""
import numpy as np
import cv2
import imutils
import os
import time
import shutil
import datetime
from flask_cors import CORS
from flask import Flask, request, jsonify, send_from_directory
from werkzeug.utils import secure_filename
import base64
import pymysql
import logging
from settings import Config

app = Flask(__name__)
CORS(app)
base_path = os.path.abspath(os.path.dirname(__file__))

app.config['UPLOAD_FOLDER'] = os.path.join(base_path, 'upload')  #上传文件
app.config['EXCEPTION_FOLDER'] = os.path.join(base_path, 'upload', 'Exception')  # 异常文件保存位置
app.config['PIC_FOLDER'] = os.path.join(base_path, 'upload', 'pic')  # 图片保存位置
app.config['LOGGING_FOLDER'] = os.path.join(base_path, 'logs')  # 日志文件
app.config['ALLOWED_EXTENSIONS'] = set(['pdf', 'PDF'])
app.config['JSON_AS_ASCII'] = False  # 返回json 支持中文

logging.basicConfig(
    level=logging.INFO,  # 控制台打印的日志级别
    filename=os.path.join(app.config['LOGGING_FOLDER'], str(time.strftime("%Y-%m", time.localtime())) + '.log'),  # 将日志写入log_new.log文件中
    filemode='a',  # 模式，有w和a，w就是写模式，每次都会重新写日志，覆盖之前的日志，a为追加，默认为追加
    format='%(asctime)s [%(levelname)s] %(filename)s[line:%(lineno)d] : %(message)s'
)

# 新建项目目录下文件夹
if not os.path.exists(app.config['UPLOAD_FOLDER']):
    os.mkdir(app.config['UPLOAD_FOLDER'])
if not os.path.exists(app.config['EXCEPTION_FOLDER']):
    os.mkdir(app.config['EXCEPTION_FOLDER'])
if not os.path.exists(app.config['PIC_FOLDER']):
    os.mkdir(app.config['PIC_FOLDER'])
if not os.path.exists(app.config['LOGGING_FOLDER']):
    os.mkdir(app.config['LOGGING_FOLDER'])

#  当前时间
# year = time.strftime('%Y')
# month = time.strftime('%m')
# day = time.strftime('%d')
today = time.strftime("%Y/%m/%d")
nowtime = time.strftime("%H:%M:%S")
year_month = time.strftime('%Y-%m')


# PDF 转换成图片
def pdf2jpg(pdf_filename, jpg_path):
    """
    :param pdf_filename: PDF文件名
    :param jpg_path: 图片保存目录，'/home/'
    :return:
    """
    from pdf2image import convert_from_path
    images = convert_from_path(pdf_filename, 300)  # dpi设置为300
    for i in range(len(images)):
        image = np.asarray(images[i])
        image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
        cv2.imwrite(os.path.join(jpg_path, str(i).zfill(5)+'.jpg'), image)
    logging.info('Successfully change pdf to image!')
    print("=======> Successfully change pdf to image!")


# 图片合成pdf
def jpeg2pdf(img_path, pdf_name):
    """
    :param img_path: 图片目录
    :param pdf_name: PDF文件名
    :return:
    """
    import img2pdf
    images = []
    for i in os.listdir(img_path):
        if i.endswith('.jpg'):
            images.append(os.path.join(img_path, i))
    with open(pdf_name, "wb") as f:
        f.write(img2pdf.convert(images))
    logging.info('Successfully merge image to pdf !')
    print("=======> Successfully merge image to pdf !")


# 支持的文件格式
def allowed_file(filename):
    return '.' in filename and str(filename.rsplit('.', 1)[1]).lower() in app.config['ALLOWED_EXTENSIONS']


# 异常文件移动到EXCEPTION_FOLDER文件夹下
def remove_file(filename, filepath):
    """
    :param filename: 文件名
    :param filepath: 原文件目录
    :return: 新文件目录
    """
    newfilepath = os.path.join(app.config['EXCEPTION_FOLDER'], filename)
    os.rename(filepath, newfilepath)
    print("文件已另存到EXCEPTION目录下")
    return newfilepath


@app.route('/compare', methods=['POST'])
def upload():
    response = {}

    # 连接数据库
    server = '127.0.0.1'
    user = 'root'
    password = 'geli*2013'
    conn = pymysql.connect(server, user, password, database='twbg')  # 获取连接
    cursor = conn.cursor()  # 获取游标

    ip = request.remote_addr
    # 检测记录写入txt
    with open(os.path.join(app.config['LOGGING_FOLDER'], year_month + '.txt'), 'a+') as f:
        f.write(ip + '   ' + today + '  ' + nowtime + '\n')

    # org_name = None
    # mod_name = None
    # org_pdfpath = None
    # mod_pdfpath = None

    # 获取设置的检测精度
    bwthresh = request.form.get('bwThresh')  # str

    if 1 <= float(bwthresh) <= 255:
        pass
    else:
        response['status'] = 1004  # 检测精度设置有误
        response['msg'] = '检测精度设置有误'
        cursor.execute(
            "INSERT INTO record202008(ip,date,time,thresh,status,errmsg) VALUES('%s','%s','%s','%s','%s','%s')"
            % (ip, today, nowtime, bwthresh, response['status'], response['msg']))
        conn.commit()
        cursor.close()
        conn.close()
        return jsonify(response)

    # 获取待对比文件
    file1 = request.files.get('file1')  # 改前文件
    file2 = request.files.get('file2')  # 改后文件

    # 判断文件类型是否正确
    if file1 and allowed_file(file1.filename):
        pass
    else:
        response['status'] = 1001  # 改前文件选择有误
        response['msg'] = '改前文件选择有误'
        cursor.execute(
            "INSERT INTO record202008(ip,date,time,thresh,status,errmsg) VALUES('%s','%s','%s','%s','%s','%s')"
            % (ip, today, nowtime, bwthresh, response['status'], response['msg']))
        conn.commit()
        cursor.close()
        conn.close()
        return jsonify(response)
    if file2 and allowed_file(file2.filename):  # 判断文件类型是否正确
        pass
    else:
        response['status'] = 1002  # 改后文件选择有误
        response['msg'] = '改后文件选择有误'
        cursor.execute(
            "INSERT INTO record202008(ip,date,time,thresh,status,errmsg) VALUES('%s','%s','%s','%s','%s','%s')"
            % (ip, today, nowtime, bwthresh, response['status'], response['msg']))
        conn.commit()
        cursor.close()
        conn.close()
        return jsonify(response)

    # 保存到服务器
    file1name = secure_filename(str(datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f"))+'_'+file1.filename)
    file1path = os.path.join(app.config['UPLOAD_FOLDER'], file1name)
    file1.save(file1path)

    file2name = secure_filename(str(datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f")) + '_' + file2.filename)
    file2path = os.path.join(app.config['UPLOAD_FOLDER'], file2name)
    file2.save(file2path)

    # 对比结果
    comp_name = os.path.splitext(file2name)[0] + '_检测结果.pdf'
    comp_path = os.path.join(app.config['UPLOAD_FOLDER'], comp_name)

    # 新建图片文件夹
    org_jpg_path = os.path.join(app.config['PIC_FOLDER'], "org_jpgs", os.path.splitext(file2name)[0])
    mod_jpg_path = os.path.join(app.config['PIC_FOLDER'], "mod_jpgs", file2name.rsplit('.', 6)[0])
    comp_jpg_path = os.path.join(app.config['PIC_FOLDER'], "comp_jpgs", file2name.rsplit('.', 6)[0])
    os.mkdir(org_jpg_path)  # file1
    os.mkdir(mod_jpg_path)  # file2
    os.mkdir(comp_jpg_path)

    # PDF转换成图片，存储到图片文件夹
    try:
        pdf2jpg(file1path, org_jpg_path)
        pdf2jpg(file2path, mod_jpg_path)
    except Exception as error:
        print("拆分pdf Failed : {0}".format(error))
        response['status'] = 404
        response['msg'] = '文件转图片失败'
        file1path1 = remove_file(file1name, file1path)
        file2path1 = remove_file(file2name, file2path)
        Excfilepath = 'file1:' + file1path1 + '\n' + 'file2:' + file2path1
        cursor.execute(
            "INSERT INTO record202008(ip,date,time,thresh,status,errmsg,exception) "
            "VALUES('%s','%s','%s','%s','%s','%s','%s')"
            % (ip, today, nowtime, bwthresh, response['status'], response['msg'], Excfilepath))
        conn.commit()
        cursor.close()
        conn.close()
        return jsonify(response)

    # 判断两文档页数是否一致，不一致返回1003
    if len(os.listdir(org_jpg_path)) == len(os.listdir(mod_jpg_path)):
        pass
    else:
        response['status'] = 1003
        response['msg'] = '文档页数不一致'
        file1path1 = remove_file(file1name, file1path)
        file2path1 = remove_file(file2name, file2path)
        Excfilepath = 'file1:' + file1path1 + '\n' + 'file2:' + file2path1
        cursor.execute(
            "INSERT INTO record202008(ip,date,time,thresh,status,errmsg,exception) "
            "VALUES('%s','%s','%s','%s','%s','%s','%s')"
            % (ip, today, nowtime, bwthresh, response['status'], response['msg'], Excfilepath))
        conn.commit()
        cursor.close()
        conn.close()
        return jsonify(response)

    for org_jpg in os.listdir(org_jpg_path):
        for mod_jpg in os.listdir(mod_jpg_path):
            # 对应图片之间进行对比
            if org_jpg == mod_jpg:
                imageA_path = os.path.join(org_jpg_path, org_jpg)
                imageB_path = os.path.join(mod_jpg_path, mod_jpg)
                # result_image_path = os.path.join(comp_jpg_path, org_jpg)  # 图片对比结果

                imageA = cv2.imread(imageA_path)
                imageB = cv2.imread(imageB_path)
                grayA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY)
                grayB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY)
                # 判断两张图尺寸是否一致，不一致返回1005
                if imageA.shape == imageB.shape:
                    pass
                else:
                    response['status'] = 1005
                    response['msg'] = '两文档纸张型号不一致'
                    file1path1 = remove_file(file1name, file1path)
                    file2path1 = remove_file(file2name, file2path)
                    Excfilepath = 'file1:' + file1path1 + '\n' + 'file2:' + file2path1
                    cursor.execute(
                        "INSERT INTO record202008(ip,date,time,thresh,status,errmsg,exception) "
                        "VALUES('%s','%s','%s','%s','%s','%s','%s')"
                        % (ip, today, nowtime, bwthresh, response['status'], response['msg'], Excfilepath))
                    conn.commit()
                    cursor.close()
                    conn.close()
                    return jsonify(response)
                try:
                    dif_img = cv2.absdiff(grayA, grayB)
                    # dif_img = np.abs(np.float(grayA) - np.float(grayB))
                    # dif_img = np.abs(grayA.astype(np.float) - grayA.astype(np.float))

                    thresh = np.uint8(dif_img / int(bwthresh) >= 1)
                    # 闭运算
                    morph_kernel = np.ones((10, 6), np.uint8)
                    closeImg = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, morph_kernel)
                    #  画差异区域的最小外接矩形
                    #  Contours: RETR_LIST,RETR_TREE,RETR_CCOMP,RETR_EXTERNAL
                    cnts = cv2.findContours(closeImg.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
                    cnts = cnts[0] if imutils.is_cv4() else cnts[1]
                    for c in cnts:
                        (x, y, w, h) = cv2.boundingRect(c)
                        cv2.rectangle(imageB, (x, y), (x + w, y + h), (0, 0, 255), 3)
                        # color:red,width:3
                    cv2.imwrite(os.path.join(comp_jpg_path, org_jpg), imageB)
                except Exception as error:
                    print("图片生成对比结果失败 : {0}".format(error))
                    response['status'] = 404
                    response['msg'] = '图片生成对比结果失败！'
                    file1path1 = remove_file(file1name, file1path)
                    file2path1 = remove_file(file2name, file2path)
                    Excfilepath = 'file1:' + file1path1 + '\n' + 'file2:' + file2path1
                    cursor.execute(
                        "INSERT INTO record202008(ip,date,time,thresh,status,errmsg,exception) "
                        "VALUES('%s','%s','%s','%s','%s','%s','%s')"
                        % (ip, today, nowtime, bwthresh, response['status'], response['msg'], Excfilepath))
                    conn.commit()
                    cursor.close()
                    conn.close()
                    return jsonify(response)

    # 图片对比结果合成PDF
    try:
        jpeg2pdf(comp_jpg_path, comp_path)
    except Exception as error:
        print("合成pdf Failed : {0}".format(error))
        response['status'] = 404
        response['msg'] = '图片生成对比结果失败！'
        file1path1 = remove_file(file1name, file1path)
        file2path1 = remove_file(file2name, file2path)
        Excfilepath = 'file1:' + file1path1 + '\n' + 'file2:' + file2path1
        cursor.execute(
            "INSERT INTO record202008(ip,date,time,thresh,status,errmsg,exception) "
            "VALUES('%s','%s','%s','%s','%s','%s','%s')"
            % (ip, today, nowtime, bwthresh, response['status'], response['msg'], Excfilepath))
        conn.commit()
        cursor.close()
        conn.close()
        return jsonify(response)

    # 删除图片文件夹
    shutil.rmtree(org_jpg_path)
    shutil.rmtree(mod_jpg_path)
    shutil.rmtree(comp_jpg_path)

    with open(comp_path, 'rb') as f1:
        base64_str = base64.b64encode(f1.read())
        # print(base64_str)

    response['status'] = 200
    response['msg'] = '检测完成！'
    # response['down_url'] = str(base64_str)
    response['show_url'] = 'http://127.0.0.1:8899/uploads/' + comp_name

    cursor.execute(
        "INSERT INTO record202008(ip,date,time,thresh,status,errmsg) VALUES('%s','%s','%s','%s','%s','%s')"
        % (ip, today, nowtime, bwthresh, response['status'], response['msg']))
    conn.commit()
    cursor.close()
    conn.close()

    return jsonify(response)


@app.route('/uploads/<filename>')
def uploaded_file(filename):
    return send_from_directory(app.config['UPLOAD_FOLDER'], filename)


if __name__ == '__main__':
    # app.run(host='10.2.10.216', port=8090, threaded=True)
    app.run(host='127.0.0.1', port=8899, debug=True)
    # app.run(debug=True)
